import Combine
import Foundation

class PurchaseBottomSheetViewModel: ObservableObject {
    private var cancellables = Set<AnyCancellable>()
    private var waitTask: Task<Void, Never>?

    @Published var items: [PurchaseProductItemFactory.Item] = []
    @Published var selectedItem: PurchaseProductItemFactory.Item?

    @Published var buttonState: ButtonState = .idle

    private let purchaseManager = Core.shared.purchaseManager

    private let finishSubject = PassthroughSubject<Void, Never>()
    private let onSubscribe: (PurchaseManager.ProductData) -> Void

    private var monthlyPrice: Decimal? {
        purchaseManager.productData.first { $0.type == .subscription && $0.period == .monthly }?.price
    }

    init(onSubscribe: @escaping ((PurchaseManager.ProductData) -> Void)) {
        self.onSubscribe = onSubscribe

        updateItems(data: purchaseManager.productData)

        purchaseManager.$purchaseData
            .receive(on: DispatchQueue.main)
            .sink { [weak self] in self?.handle(purchases: $0)
            }
            .store(in: &cancellables)

        purchaseManager.$productData
            .receive(on: DispatchQueue.main)
            .sink { [weak self] products in
                self?.updateItems(data: products)
            }
            .store(in: &cancellables)

        purchaseManager.$usedOfferProductIds
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.updateItems(data: self?.purchaseManager.productData ?? [])
            }
            .store(in: &cancellables)
    }

    private func updateItems(data: [PurchaseManager.ProductData]) {
//        print(data)
        items = data
            .sorted { $0.order < $1.order }
            .compactMap {
                let used = purchaseManager.usedOfferProductIds.contains($0.id)
                return PremiumFactory.productItemFactory.item(product: $0, offerWasUsed: used, monthlyPrice: monthlyPrice)
            }

        selectedItem = selectedItem ?? items.first
    }

    @MainActor private func update(state: ButtonState) async {
        await MainActor.run { [weak self] in
            self?.buttonState = state
        }
    }

    private func waitTransaction() {
        waitTask?.cancel()

        waitTask = Task { [weak self] in
            await self?.update(state: .loading)

            do {
                try await Task.sleep(nanoseconds: 5_000_000_000)

                if !Task.isCancelled {
                    await self?.update(state: .idle)
                }
            } catch {
                await self?.update(state: .idle)
            }
        }
    }

    @MainActor
    private func handleSuccessSubscription(_ product: PurchaseManager.ProductData) async {
        onSubscribe(product)
        finishSubject.send()
    }

    private func handle(purchases _: [PurchaseManager.PurchaseData]) {
        if let purchase = purchaseManager.activePurchase, let product = purchaseManager.productData.first(where: { $0.id == purchase.id }) {
            waitTask?.cancel()
            waitTask = nil

            Task {
                await handleSuccessSubscription(product)
            }
        }
    }

    func subscribe() {
        Task { [weak self] in
            await self?.update(state: .loading)

            do {
                guard let selectedItem = self?.selectedItem else {
                    return
                }

                try await self?.purchaseManager.purchase(product: selectedItem.product)

                await self?.update(state: .idle)
                await self?.handleSuccessSubscription(selectedItem.product)
            } catch {
                print("ERROR: \(error)") // TODO: Handle error
                await self?.update(state: .idle)
            }
        }
    }
}

extension PurchaseBottomSheetViewModel {
    var finishPublisher: AnyPublisher<Void, Never> {
        finishSubject.eraseToAnyPublisher()
    }

    func set(item: PurchaseProductItemFactory.Item) {
        selectedItem = item
    }

    func handleRedeemCode(result: Result<Void, any Error>) {
        switch result {
        case .success(): // wait transaction and handle it
            waitTransaction()
        case let .failure(error): // don't doing anything
            print(error)
        }
    }
}

extension PurchaseBottomSheetViewModel {
    enum ButtonState {
        case idle
        case loading
    }
}

extension PurchaseManager.SubscriptionPeriod: Identifiable {
    var id: String { rawValue }

    var title: String {
        switch self {
        case .annually: return "purchase.period.annually".localized
        case .monthly: return "purchase.period.monthly".localized
        }
    }

    var pricePeriod: String {
        switch self {
        case .annually: return "purchase.period.year".localized
        case .monthly: return "purchase.period.month".localized
        }
    }
}

extension PurchaseManager.ProductData {
    var order: Int {
        switch type {
        case .lifetime: return 2
        case .subscription:
            switch period {
            case .monthly: return 1
            case .annually: return 0
            case .none: return 3
            }
        }
    }
}
